import { RequestUtil } from 'napcat-common/src/request';
import {
  GroupEssenceMsgRet,
  InstanceContext,
  WebApiGroupMember,
  WebApiGroupMemberRet,
  WebApiGroupNoticeRet,
  WebHonorType, NapCatCore,
} from '@/napcat-core/index';

import { createReadStream, readFileSync, statSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { basename } from 'node:path';
import { qunAlbumControl } from '../data/webapi';
import { createAlbumCommentRequest, createAlbumFeedPublish, createAlbumMediaFeed } from '../data/album';
export class NTQQWebApi {
  context: InstanceContext;
  core: NapCatCore;

  constructor (context: InstanceContext, core: NapCatCore) {
    this.context = context;
    this.core = core;
  }

  async shareDigest (groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
    const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
            bkn: this.getBknFromCookie(cookieObject),
            group_code: groupCode,
            msg_seq: msgSeq,
            msg_random: msgRandom,
            target_group_code: targetGroupCode,
        }).toString()}`;
    try {
      return RequestUtil.HttpGetText(url, 'GET', '', { Cookie: this.cookieToString(cookieObject) });
    } catch {
      return undefined;
    }
  }

  async getGroupEssenceMsgAll (GroupCode: string) {
    const ret: GroupEssenceMsgRet[] = [];
    for (let i = 0; i < 20; i++) {
      const data = await this.getGroupEssenceMsg(GroupCode, i, 50);
      if (!data) break;
      ret.push(data);
      if (data.data.is_end) break;
    }
    return ret;
  }

  async getGroupEssenceMsg (GroupCode: string, page_start: number = 0, page_limit: number = 50) {
    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
    const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
            bkn: this.getBknFromCookie(cookieObject),
            page_start: page_start.toString(),
            page_limit: page_limit.toString(),
            group_code: GroupCode,
        }).toString()}`;
    try {
      const ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(
        url,
        'GET',
        '',
        { Cookie: this.cookieToString(cookieObject) }
      );
      return ret.retcode === 0 ? ret : undefined;
    } catch {
      return undefined;
    }
  }

  async getGroupMembers (GroupCode: string): Promise<WebApiGroupMember[]> {
    // logDebug('webapi 获取群成员', GroupCode);
    const memberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>();
    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
    const retList: Promise<WebApiGroupMemberRet>[] = [];
    const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
            `https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
                st: '0',
                end: '40',
                sort: '1',
                gc: GroupCode,
                bkn: this.getBknFromCookie(cookieObject),
            }).toString()}`,
            'POST',
            '',
            { Cookie: this.cookieToString(cookieObject) }
    );
    if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
      return [];
    } else {
      for (const key in fastRet.mems) {
        if (fastRet.mems[key]) {
          memberData.push(fastRet.mems[key]);
        }
      }
    }
    // 初始化获取PageNum
    const PageNum = Math.ceil(fastRet.count / 40);
    // 遍历批量请求
    for (let i = 2; i <= PageNum; i++) {
      const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
                `https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
                    st: ((i - 1) * 40).toString(),
                    end: (i * 40).toString(),
                    sort: '1',
                    gc: GroupCode,
                    bkn: this.getBknFromCookie(cookieObject),
                }).toString()}`,
                'POST',
                '',
                { Cookie: this.cookieToString(cookieObject) }
      );
      retList.push(ret);
    }
    // 批量等待
    for (let i = 1; i <= PageNum; i++) {
      const ret = await (retList[i]);
      if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) {
        continue;
      }
      for (const key in ret.mems) {
        if (ret.mems[key]) {
          memberData.push(ret.mems[key]);
        }
      }
    }
    return memberData;
  }

  // public  async addGroupDigest(groupCode: string, msgSeq: string) {
  //   const url = `https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&msg_seq=${msgSeq}&msg_random=444021292`;
  //   const res = await this.request(url);
  //   return await res.json();
  // }

  // public async getGroupDigest(groupCode: string) {
  //   const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&page_start=0&page_limit=20`;
  //   const res = await this.request(url);
  //   return await res.json();
  // }

  async setGroupNotice (
    GroupCode: string,
    Content: string,
    pinned: number = 0,
    type: number = 1,
    is_show_edit_card: number = 1,
    tip_window_type: number = 1,
    confirm_required: number = 1,
    picId: string = '',
    imgWidth: number = 540,
    imgHeight: number = 300
  ) {
    interface SetNoticeRetSuccess {
      ec: number;
      em: string;
      id: number;
      ltsm: number;
      new_fid: string;
      read_only: number;
      role: number;
      srv_code: number;
    }

    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');

    try {
      const settings = JSON.stringify({
        is_show_edit_card,
        tip_window_type,
        confirm_required,
      });
      const externalParam = {
        pic: picId,
        imgWidth: imgWidth.toString(),
        imgHeight: imgHeight.toString(),
      };
      const ret: SetNoticeRetSuccess = await RequestUtil.HttpGetJson<SetNoticeRetSuccess>(
                `https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?${new URLSearchParams({
                    bkn: this.getBknFromCookie(cookieObject),
                    qid: GroupCode,
                    text: Content,
                    pinned: pinned.toString(),
                    type: type.toString(),
                    settings,
                    ...(picId === '' ? {} : externalParam),
                }).toString()}`,
                'POST',
                '',
                { Cookie: this.cookieToString(cookieObject) }
      );
      return ret;
    } catch {
      return undefined;
    }
  }

  async getGroupNotice (GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
    try {
      const ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(
                `https://web.qun.qq.com/cgi-bin/announce/get_t_list?${new URLSearchParams({
                    bkn: this.getBknFromCookie(cookieObject),
                    qid: GroupCode,
                    ft: '23',
                    ni: '1',
                    n: '1',
                    i: '1',
                    log_read: '1',
                    platform: '1',
                    s: '-1',
                }).toString()}&n=20`,
                'GET',
                '',
                { Cookie: this.cookieToString(cookieObject) }
      );
      return ret?.ec === 0 ? ret : undefined;
    } catch {
      return undefined;
    }
  }

  private async getDataInternal (cookieObject: { [key: string]: string }, groupCode: string, type: number) {
    let resJson;
    try {
      const res = await RequestUtil.HttpGetText(
                `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
                    gc: groupCode,
                    type: type.toString(),
                }).toString()}`,
                'GET',
                '',
                { Cookie: this.cookieToString(cookieObject) }
      );
      const match = /window\.__INITIAL_STATE__=(.*?);/.exec(res);
      if (match?.[1]) {
        resJson = JSON.parse(match[1].trim());
      }
      return type === 1 ? resJson?.talkativeList : resJson?.actorList;
    } catch (e) {
      this.context.logger.logDebug('获取当前群荣耀失败', e);
      return undefined;
    }
  }

  private async getHonorList (cookieObject: { [key: string]: string }, groupCode: string, type: number) {
    const data = await this.getDataInternal(cookieObject, groupCode, type);
    if (!data) {
      this.context.logger.logError(`获取类型 ${type} 的荣誉信息失败`);
      return [];
    }
    return data.map((item: {
      uin: string,
      name: string,
      avatar: string,
      desc: string,
    }) => ({
      user_id: item?.uin,
      nickname: item?.name,
      avatar: item?.avatar,
      description: item?.desc,
    }));
  }

  async getGroupHonorInfo (groupCode: string, getType: WebHonorType) {
    const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
    const HonorInfo = {
      group_id: Number(groupCode),
      current_talkative: {},
      talkative_list: [],
      performer_list: [],
      legend_list: [],
      emotion_list: [],
      strong_newbie_list: [],
    };

    if (getType === WebHonorType.TALKATIVE || getType === WebHonorType.ALL) {
      const talkativeList = await this.getHonorList(cookieObject, groupCode, 1);
      if (talkativeList.length > 0) {
        HonorInfo.current_talkative = talkativeList[0];
        HonorInfo.talkative_list = talkativeList;
      }
    }

    if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
      HonorInfo.performer_list = await this.getHonorList(cookieObject, groupCode, 2);
    }

    if (getType === WebHonorType.LEGEND || getType === WebHonorType.ALL) {
      HonorInfo.legend_list = await this.getHonorList(cookieObject, groupCode, 3);
    }

    if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
      HonorInfo.emotion_list = await this.getHonorList(cookieObject, groupCode, 6);
    }

    // 冒尖小春笋好像已经被tx扬了 R.I.P.
    if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
      HonorInfo.strong_newbie_list = [];
    }

    return HonorInfo;
  }

  private cookieToString (cookieObject: { [key: string]: string }) {
    return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ');
  }

  public getBknFromCookie (cookieObject: { [key: string]: string }) {
    const sKey = cookieObject['skey'] as string;

    let hash = 5381;
    for (let i = 0; i < sKey.length; i++) {
      const code = sKey.charCodeAt(i);
      hash = hash + (hash << 5) + code;
    }
    return (hash & 0x7FFFFFFF).toString();
  }

  public getBknFromSKey (sKey: string) {
    let hash = 5381;
    for (let i = 0; i < sKey.length; i++) {
      const code = sKey.charCodeAt(i);
      hash = hash + (hash << 5) + code;
    }
    return (hash & 0x7FFFFFFF).toString();
  }

  async getAlbumListByNTQQ (gc: string) {
    return await this.context.session.getAlbumService().getAlbumList({
      qun_id: gc,
      attach_info: '',
      seq: 3331,
      request_time_line: {
        request_invoke_time: '0',
      },
    });
  }

  async getAlbumList (gc: string) {
    const skey = await this.core.apis.UserApi.getSKey() || '';
    const pskey = (await this.core.apis.UserApi.getPSkey(['qzone.qq.com'])).domainPskeyMap.get('qzone.qq.com') || '';
    const bkn = this.getBknFromSKey(skey);
    const uin = this.core.selfInfo.uin || '10001';
    const cookies = `p_uin=o${this.core.selfInfo.uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin} `;
    const api = 'https://h5.qzone.qq.com/proxy/domain/u.photo.qzone.qq.com/cgi-bin/upp/qun_list_album_v2?';
    const params = new URLSearchParams({
      random: '7570',
      g_tk: bkn,
      format: 'json',
      inCharset: 'utf-8',
      outCharset: 'utf-8',
      qua: 'V1_IPH_SQ_6.2.0_0_HDBM_T',
      cmd: 'qunGetAlbumList',
      qunId: gc,
      qunid: gc,
      start: '0',
      num: '1000',
      uin,
      getMemberRole: '0',
    });
    const response = await RequestUtil.HttpGetJson<{ data: { album: Array<{ id: string, title: string }> } }>(api + params.toString(), 'GET', '', {
      Cookie: cookies,
    });
    return response.data.album;
  }

  async createQunAlbumSession (gc: string, sAlbumID: string, sAlbumName: string, path: string, skey: string, pskey: string, img_md5: string, uin: string) {
    const img = readFileSync(path);
    const img_size = img.length;
    const img_name = basename(path);
    const GTK = this.getBknFromSKey(skey);
    const cookie = `p_uin=o${uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin}`;
    const body = qunAlbumControl({
      uin,
      group_id: gc,
      pskey,
      pic_md5: img_md5,
      img_size,
      img_name,
      sAlbumName,
      sAlbumID,
    });
    const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileBatchControl/${img_md5}?g_tk=${GTK}`;
    const post = await RequestUtil.HttpGetJson<{ data: { session: string }, ret: number, msg: string }>(api, 'POST', body, {
      Cookie: cookie,
      'Content-Type': 'application/json',
    });
    return post;
  }

  async uploadQunAlbumSlice (path: string, session: string, skey: string, pskey: string, uin: string, slice_size: number) {
    const img_size = statSync(path).size;
    let seq = 0;
    let offset = 0;
    const GTK = this.getBknFromSKey(skey);
    const cookie = `p_uin=o${uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin}`;

    const stream = createReadStream(path, { highWaterMark: slice_size });

    for await (const chunk of stream) {
      const end = Math.min(offset + chunk.length, img_size);
      const form = new FormData();
      form.append('uin', uin);
      form.append('appid', 'qun');
      form.append('session', session);
      form.append('offset', offset.toString());
      form.append('data', new Blob([chunk], { type: 'application/octet-stream' }), 'blob');
      form.append('checksum', '');
      form.append('check_type', '0');
      form.append('retry', '0');
      form.append('seq', seq.toString());
      form.append('end', end.toString());
      form.append('cmd', 'FileUpload');
      form.append('slice_size', slice_size.toString());
      form.append('biz_req.iUploadType', '0');

      const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileUpload?seq=${seq}&retry=0&offset=${offset}&end=${end}&total=${img_size}&type=form&g_tk=${GTK}`;
      const response = await fetch(api, {
        method: 'POST',
        headers: {
          Cookie: cookie,
        },
        body: form,
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const post = await response.json() as { ret: number, msg: string }; if (post.ret !== 0) {
        throw new Error(`分片 ${seq} 上传失败: ${post.msg}`);
      }
      offset += chunk.length;
      seq++;
    }

    return { success: true, message: '上传完成' };
  }

  async uploadImageToQunAlbum (gc: string, sAlbumID: string, sAlbumName: string, path: string) {
    const skey = await this.core.apis.UserApi.getSKey() || '';
    const pskey = (await this.core.apis.UserApi.getPSkey(['qzone.qq.com'])).domainPskeyMap.get('qzone.qq.com') || '';
    const img_md5 = createHash('md5').update(readFileSync(path)).digest('hex');
    const uin = this.core.selfInfo.uin || '10001';
    const session = (await this.createQunAlbumSession(gc, sAlbumID, sAlbumName, path, skey, pskey, img_md5, uin)).data.session;
    if (!session) throw new Error('创建群相册会话失败');
    await this.uploadQunAlbumSlice(path, session, skey, pskey, uin, 16384);
  }

  async getAlbumMediaListByNTQQ (gc: string, albumId: string, attach_info: string = '') {
    return (await this.context.session.getAlbumService().getMediaList({
      qun_id: gc,
      attach_info,
      seq: 0,
      request_time_line: {
        request_invoke_time: '0',
      },
      album_id: albumId,
      lloc: '',
      batch_id: '',
    })).response;
  }

  async doAlbumMediaPlainCommentByNTQQ (
    qunId: string,
    albumId: string,
    lloc: string,
    content: string) {
    const random_seq = Math.floor(Math.random() * 9000) + 1000;
    const uin = this.core.selfInfo.uin || '10001';
    // 16位number数字
    const client_key = Date.now() * 1000;
    return await this.context.session.getAlbumService().doQunComment(
      random_seq, {
        map_info: [],
        map_bytes_info: [],
        map_user_account: [],
      },
      qunId,
      2,
      createAlbumMediaFeed(uin, albumId, lloc),
      createAlbumCommentRequest(uin, content, client_key)
    );
  }

  async deleteAlbumMediaByNTQQ (
    qunId: string,
    albumId: string,
    lloc: string) {
    const random_seq = Math.floor(Math.random() * 9000) + 1000;
    return await this.context.session.getAlbumService().deleteMedias(
      random_seq,
      qunId,
      albumId,
      [lloc],
      []
    );
  }

  async doAlbumMediaLikeByNTQQ (
    qunId: string,
    albumId: string,
    lloc: string,
    id: string) {
    const random_seq = Math.floor(Math.random() * 9000) + 1000;
    const uin = this.core.selfInfo.uin || '10001';
    return await this.context.session.getAlbumService().doQunLike(
      random_seq, {
        map_info: [],
        map_bytes_info: [],
        map_user_account: [],
      }, {
        id,
        status: 1,
      },
      createAlbumFeedPublish(qunId, uin, albumId, lloc)
    );
  }
}
